为什么需要日志
日志系统的核心目标是回答三个问题:什么时间、发生了什么、结果如何。无论是在前端使用 console.log 调试,还是在服务端记录运行时状态,日志的本质都是帮开发者快速定位问题。
在服务端场景中,日志的重要性远超前端——生产环境没有浏览器 DevTools,不可能 24 小时盯着控制台输出,必须依赖结构化的日志系统来完成错误追踪、行为审计和性能分析。
本章节学习路线
| 序号 | 主题 | 内容 |
|---|---|---|
| 1 | 日志基础 | 日志等级、功能分类、记录位置 |
| 2 | NestJS 内置 Logger | ConsoleLogger 的用法与局限 |
| 3 | Winston | 功能全面的日志库,适合需要多传输通道的场景 |
| 4 | Pino | 高性能日志库,5 倍于 Winston 的吞吐量,推荐生产环境 |
| 5 | 业务系统集成 | 操作日志、请求日志、持久化方案 |
日志等级
NestJS 内置 Logger 提供六个等级,从低到高:
| 等级 | 方法 | 典型场景 |
|---|---|---|
verbose | logger.verbose() | 最细粒度的追踪信息 |
debug | logger.debug() | 开发调试,如数据库加载过程 |
log | logger.log() | 通用信息,等同于 info |
warn | logger.warn() | 可恢复的异常,如废弃 API 警告、多次重试 |
error | logger.error() | 需要关注的错误,如数据库连接失败 |
fatal | logger.fatal() | 不可恢复的崩溃 |
这些等级名称在 Winston 和 Pino 中基本一致(NestJS 的 verbose 对应 Pino 的 trace),可以直接使用对应方法输出不同级别的日志。
Pino 与 Winston 等级映射
| NestJS | Pino | Winston (npm) |
|---|---|---|
verbose | trace (10) | silly |
debug | debug (20) | debug |
log | info (30) | info |
warn | warn (40) | warn |
error | error (50) | error |
fatal | fatal (60) | — |
日志按功能分类
错误日志
定位问题、排查故障的核心。记录程序异常、资源访问失败、外部服务调用超时等。生产环境中,错误日志必须持久化存储,控制台输出一旦滚屏就无法追溯。
调试日志
开发阶段专用,记录变量值、函数调用链、中间状态等。上线后应关闭或降低到 debug 以下等级,避免日志量爆炸。
请求日志
记录 HTTP 请求的方法、路径、响应状态码和耗时。用于:
- 追踪用户操作链路(特别是敏感操作如删除、支付)
- 审计合规(金融、医疗等行业要求全量请求记录)
- 性能分析(慢接口定位)
金融系统中几乎所有操作都配有请求日志,方便回溯交易链路。
日志记录位置
控制台(Console)
开发阶段的主要输出位置。优点是即时可见、零配置;缺点是不可持久化、服务重启后丢失。容器化环境(Docker/K8s)中,stdout 输出可被 Fluent Bit、Promtail 等工具自动收集。
文件(File)
生产环境必备。通过日志滚动(log rotation)控制文件大小和保留天数,防止磁盘被撑满。Winston 的 winston-daily-rotate-file 和 Pino 的 pino-transport-file-rotate 都支持按日期/大小切割。
// Winston DailyRotateFile 示例
const transport = new DailyRotateFile({
filename: 'application-%DATE%.log',
datePattern: 'YYYY-MM-DD',
maxFiles: '30d', // 保留 30 天
maxSize: '20m', // 单文件 20MB 切割
level: 'info',
});
typescript
数据库(Database)
记录需要长期查询和分析的结构化数据,如用户操作审计、接口调用统计。推荐使用 Elasticsearch、ClickHouse 等专用于日志检索的存储引擎,而非直接写入业务数据库。
环境与等级推荐配置
| 环境 | 启用的等级 | 说明 |
|---|---|---|
| development | verbose, debug, log, warn, error, fatal | 全量输出,方便调试 |
| staging | log, warn, error | 平衡可见性与日志量 |
| production | warn, error, fatal | 仅输出需要关注的日志 |
// NestJS 环境配置
const app = await NestFactory.create(AppModule, {
logger: process.env.NODE_ENV === 'production'
? ['warn', 'error', 'fatal']
: ['verbose', 'debug', 'log', 'warn', 'error', 'fatal'],
});
typescript
等级 × 环境 × 存储位置对照
| 等级 | 开发环境 | 测试环境 | 生产环境 | 存储位置 |
|---|---|---|---|---|
log (通用) | ✅ | ✅ | ❌ | Console |
error | ✅ | ✅ | ✅ | File / DB |
warn | ✅ | ✅ | ✅ | Console + File |
debug | ✅ | ✅ | ❌ | Console + File |
verbose | ✅ | ❌ | ❌ | Console |
| API 请求 | ❌ | ✅ | ✅ | Console / DB |
Winston vs Pino 选型建议
| 维度 | Winston | Pino |
|---|---|---|
| 吞吐量 | ~10,000 logs/sec | ~50,000 logs/sec(5 倍) |
| JSON 输出 | 需配置 format | 默认 JSON |
| 内置传输通道 | 丰富(File、HTTP、DailyRotate 等) | 较少,通过 transport 扩展 |
| NestJS 集成 | nest-winston | nestjs-pino v4.6.x |
| 自动请求上下文 | 需手动实现 | AsyncLocalStorage 自动绑定 |
| 学习曲线 | 低 | 中等 |
| 推荐场景 | 简单应用、需要多种输出通道 | 生产环境首选 |
2025/2026 共识:Pino 是 NestJS 生产环境的首选日志方案,驱动因素是 5 倍性能优势、JSON-first 输出与现代可观测性栈(ELK/Loki/Datadog)的天然契合。
日志生产环境架构
应用 (Pino/JSON) → stdout → Fluent Bit/Promtail → Grafana Loki/Elasticsearch → Grafana/Kibana
text
关键规则:
- 永远不记录敏感数据(密码、Token、PII)
- 生产环境一律使用结构化 JSON 格式
- 每条日志包含关联 ID(request ID、trace ID),
nestjs-pino的PinoLogger.assign()可自动传播上下文 - 异步写入,避免阻塞事件循环
↑